#ifndef MODIFIER_H
#define MODIFIER_H
#include "constants.h"
#include "checker.h"
#include "event.h"
#include <cstdlib>
#include <vector>
#include <set>
#include <boost/ptr_container/ptr_vector.hpp>

class IModifier
{
public:
    virtual bool getVertex(ProcessState& state, size_t v, EventQueue& q) = 0;
    virtual bool undoGetVertex(ProcessState& state, size_t v, EventQueue& q) = 0;
    virtual bool notGetVertex(ProcessState& state, size_t v, EventQueue& q) = 0;
    virtual bool undoNotGetVertex(ProcessState& state, size_t v, EventQueue& q) = 0;
    virtual void setChecker(std::auto_ptr<IChecker> checker)
    {
        checker_ = checker;
    }
    virtual ~IModifier() {}
protected:
    std::auto_ptr<IChecker> checker_;
};

class VertexModifier: public IModifier
{
public:
    VertexModifier(std::auto_ptr<IChecker> checker)
        {checker_ = checker;}
    VertexModifier(IChecker* checker)
        {checker_ = std::auto_ptr<IChecker>(checker);}
    bool getVertex(ProcessState& state, size_t v, EventQueue& q)
    {
        state.vertecies[state.numVertecies++] = v;
        state.getted[v] = 1;
        return checker_->check(state);
    }
    bool undoGetVertex(ProcessState& state, size_t v, EventQueue& q)
    {
        state.numVertecies--;
        state.getted[v] = -1;
        return true;
    }
    bool notGetVertex(ProcessState& state, size_t v, EventQueue& q)
    {
        state.numPotencialVertecies--;
        state.getted[v] = 0;
        return !state.requiredVertices[v];
    }
    bool undoNotGetVertex(ProcessState& state, size_t v, EventQueue& q)
    {
        state.numPotencialVertecies++;
        state.getted[v] = -1;
        return true;
    }
};

class DegreeModifier: public IModifier
{
public:
    bool getVertex(ProcessState& state, size_t v, EventQueue& q)
    {
        if (state.getDegree[v] > 0) {
            return false;
        }
        const std::vector<size_t>& inc = state.graph->incendents(v);
        for(size_t i = 0; i < inc.size(); ++i) {
            size_t u = inc[i];
            state.getDegree[u]++;
            if (inc[i] > v && state.getDegree[inc[i]] == 1) {
                q.push(Event(NOT_GET_VERTEX, inc[i]));
            }
            state.notGetDegree[u]--;
            state.notProcessedDegree[u]--;
        }
        return true;
    }
    bool undoGetVertex(ProcessState& state, size_t v, EventQueue& q)
    {
        if (state.getDegree[v] > 0) {
            return true;
        }
        const std::vector<size_t>& inc = state.graph->incendents(v);
        for(size_t i = 0; i < inc.size(); ++i) {
            size_t u = inc[i];
            state.getDegree[u]--;
            /*if (inc[i] > v && state.getDegree[inc[i]] == 0) {
                q.push(Event(UNDO_NOT_GET_VERTEX, inc[i]));
            }*/
            state.notGetDegree[u]++;
            state.notProcessedDegree[u]++;
        }
        return true;
    }
    bool notGetVertex(ProcessState& state, size_t v, EventQueue& q)
    {
        bool res = true;
        const std::vector<size_t>& inc = state.graph->incendents(v);
        for(size_t i = 0; i < inc.size(); ++i) {
            size_t u = inc[i];
            state.notProcessedDegree[u]--;
            //not getted without getted or not processed incendent.
            if (state.getted[u] == 0 && state.getDegree[u] == 0 &&
                state.notProcessedDegree[u] == 0) {
                res = false;
            }
        }
        return res;
    }
    bool undoNotGetVertex(ProcessState& state, size_t v, EventQueue& q)
    {
        const std::vector<size_t>& inc = state.graph->incendents(v);
        for(size_t i = 0; i < inc.size(); ++i) {
            state.notProcessedDegree[inc[i]]++;
        }
        return true;
    }
};

/*class DimensionModifier: public IModifier
{
public:
    bool getVertex(ProcessState& state, size_t v, EventQueue& q)
    {
        return true;
    }
    bool undoGetVertex(ProcessState& state, size_t v, EventQueue& q)
    {
        return true;
    }
    bool notGetVertex(ProcessState& state, size_t v, EventQueue& q)
    {
        if (v & (1 << (n - 1))) {
            --state.numVerteciesWithLargestBit;
        }
        return checker.check(state);
    }
    bool undoNotGetVertex(ProcessState& state, size_t v, EventQueue& q)
    {
        if (v & (1 << (n - 1))) {
            ++state.numVerteciesWithLargestBit;
        }
        return true;
    }
private:
    OnlineDimensionChecker checker;
};*/

class PotentialDegreeModifier: public IModifier
{
public:
    PotentialDegreeModifier(){}
    PotentialDegreeModifier(size_t maxDegree): checker(maxDegree) {}
    bool getVertex(ProcessState& state, size_t v, EventQueue& q)
    {
        return true;
    }
    bool undoGetVertex(ProcessState& state, size_t v, EventQueue& q)
    {
        return true;
    }
    bool notGetVertex(ProcessState& state, size_t v, EventQueue& q)
    {
        return notGetVertex(state, v, 1);
    }
    bool undoNotGetVertex(ProcessState& state, size_t v, EventQueue& q)
    {
        return notGetVertex(state, v, -1);
    }
private:
    bool notGetVertex(ProcessState& state, size_t v, int dir)
    {
        PotentialDegree& pdegree = state.potentialDegree;
        if (dir == 1) {
            pdegree.degreeSet.del(pdegree.degree[v]);
        } else {
            pdegree.degreeSet.add(pdegree.degree[v]);
        }
        const std::vector<size_t>& inc = state.newGraph->incendents(v);
        for(size_t i = 0; i < inc.size(); ++i) {
            size_t u = inc[i];
            if (state.getted[u] != 0) {
                pdegree.degreeSet.change(pdegree.degree[u],
                                         pdegree.degree[u] - dir);
            }
            pdegree.degree[u] -= dir;
        }
        if (dir == 1) {
            return checker.check(state);
        } else {
            return true;
        }
    }
    OnlineMaxDegreeChecker checker;
};

class ColoringModifier: public IModifier
{
public:
    ColoringModifier(){}
    ColoringModifier(size_t maxColor): checker(maxColor) {}
    bool getVertex(ProcessState& state, size_t v, EventQueue& q)
    {
        return true;
    }
    bool undoGetVertex(ProcessState& state, size_t v, EventQueue& q)
    {
        return true;
    }
    bool notGetVertex(ProcessState& state, size_t v, EventQueue& q)
    {
        return notGetVertex(state, v, 1);
    }
    bool undoNotGetVertex(ProcessState& state, size_t v, EventQueue& q)
    {
        return notGetVertex(state, v, -1);
    }
private:

    bool changeColor(ProcessState& state, size_t v, size_t newColor,
        std::vector<size_t>& modifs)
    {
        ColorInfo& info = state.colorInfo;
        if (info.color[v] == newColor || state.getted[v] == 0) {
            return false;
        }
        info.maxColor.change(info.color[v], newColor);
        const std::vector<size_t>& inc = state.newGraph->incendents(v);
        for(size_t i = 0; i < inc.size(); ++i) {
            size_t u = inc[i];
            if (u < v) continue;
            if (info.neighbours[u].change(info.color[v], newColor)) {
                modifs.push_back(u);
            }
        }
        info.color[v] = newColor;
        return true;
    }
    
    bool notGetVertex(ProcessState& state, size_t v, int dir)
    {
        ColorInfo& info = state.colorInfo;
            
        if (dir == 1) {
            info.maxColor.del(info.color[v]);
        } else {
            info.color[v] = std::min(info.neighbours[v].minOutside(), n + 2);
            info.maxColor.add(info.color[v]);
        }

        std::vector<size_t> modifs;

        const std::vector<size_t>& inc = state.newGraph->incendents(v);
        for(size_t i = 0; i < inc.size(); ++i) {
            size_t u = inc[i];
            if (u < v) continue;
            bool isModif;
            if (dir == 1) {
                isModif = info.neighbours[u].del(info.color[v]);
            } else {
                isModif = info.neighbours[u].add(info.color[v]);
            }
            if (isModif) {
                modifs.push_back(u);
            }
        }
        for(size_t i = 0; i < modifs.size(); ++i) {
            size_t u = modifs[i];
            changeColor(state, u,
                std::min(info.neighbours[u].minOutside(), n + 2), modifs);
        }
        if (dir == 1) {
            return checker.check(state);
        } else {
            return true;
        }
    }
    OnlineColoringChecker checker;
};

#endif//MODIFIER_H
